//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<debug.h>
///	@path	~/src/lib/utilities/
///	@date	2007/06/22
///	@desc	Debug helpers, including trace, tracking & functions.

#pragma once

#include "xeres.h"

#include <stdarg.h>
#include <string>
#include <cassert>

#include "lib/utilities/string.h"
#include "lib/utilities/helper.h"
#include "lib/utilities/callback.h"

namespace xeres {

	/*!
		\ingroup lib_module
	*/

	/// Trace output level
	enum TraceLevel
	{
		/// \brief Common trace.
		TRACE_LV_TRACE ,
		/// \brief For debug outputs.
		TRACE_LV_DEBUG ,
		/// \brief For tracking program states or activities.
		TRACE_LV_INFO ,
		/// \brief Warning, but not error, may cause problem.
		TRACE_LV_WARNING ,
		/// \brief Error, but recovered, program can pass thought.
		TRACE_LV_ERROR ,
		/// \brief Fatal error, cannot be recovered, program must be terminated.
		TRACE_LV_FATAL ,
		/// \brief Console outputs.
		TRACE_LV_CONSOLE ,
		/// \brief Assertion outputs.
		TRACE_LV_ASSERT ,
		/// \brief Stdout stream catched.
		TRACE_LV_STDOUT ,
		/// \brief Stderr stream catched.
		TRACE_LV_STDERR ,
		//
		TRACE_LV_last
	};

	// Trace category
	class TraceCategory
	{
	public:

		/// \ctor
		TraceCategory( const wchar_t * category , TraceLevel default_level );

		/// \dtor
		inline ~TraceCategory( void ) {}

		/// \brief Get category debug level.
		inline TraceLevel GetLevel( void ) const {
			return m_traceCategoryLevel == 0 ? TRACE_LV_TRACE : *m_traceCategoryLevel;
		}

		/// \brief Get debug category full path.
		inline const WString& GetCategory( void ) const {
			return m_category;
		}

	private:

		// name
		WString			m_category;

		// trace category level pointer
		TraceLevel *	m_traceCategoryLevel;
	};

	/*!
		\brief		Lowest level trace output function, directly outputs information to trace.
	*/
	void trace_puts( const wchar_t * info );

	/*!
		\brief		Output trace information with unformatted string & argument list.
	*/
	void trace_vprintf( const wchar_t * fmt , va_list ap );

	/*!
		\brief		High level trace call.
	*/
	void trace_printf( const wchar_t * fmt , ... );

	/*!
		\brief		For stdout redirection.
	*/
	void trace_stdout( const char * msg );

	/*!
		\brief		For stderr redirection.
	*/
	void trace_stderr( const char * msg );

	/*!
		\brief		Add additional trace message handler.
	*/
	int add_trace_handler( function<void(TraceLevel,const wchar_t*)> callback );

	/*!
		\brief		Delete additional trace message handler.
	*/
	void del_trace_handler( int id );


	// A dummy class to hold context depend outputs.
	// Input TraceCategory to filter out some informations.
	class TraceOutput
	{
	public:

		/// \ctor
		TraceOutput( TraceLevel level , const TraceCategory * category );

		// Do output trace messages.
		void Trace( const wchar_t * fmt , ... );

	private:

		TraceLevel				m_level;
		const TraceCategory&	m_category;
	};

	/*!
		\brief		Fatal call, terminates program immediately.
	*/
	void fatal_abort( const wchar_t * info , const wchar_t * file , const wchar_t * function );

#ifndef _DOC_GEN

	// fatal handlers
	typedef void (__cdecl * FatalHandlerFn)( const wchar_t * info , const wchar_t * file , const wchar_t * function );

	// crash callback
	struct CrashContext;
	typedef void (__cdecl * CrashCallbackFn)( const wchar_t * filename , CrashContext * context );

	// crash dump file callback
	typedef void (__cdecl * CrashHandlerFn)( CrashCallbackFn callback , CrashContext * context );

#endif /*_DOC_GEN*/

	/*!
		\brief		Set specific handler to handle fatal situation.
	*/
	void set_fatal_handler( FatalHandlerFn handler );

	/*! 
		\brief		Set crash dump file callback.
	*/
	void set_crash_dump_handler( CrashHandlerFn handler );

#ifdef	_DEBUG

// define debug category of current module

// a global default trace category
	static TraceCategory * __pTraceCategory = NULL;

	/*!
		\def	TRACE_CATEGORY( CAT , LEVEL )
		\brief	Define current compilation unit's trace category.
	*/
#	define	TRACE_CATEGORY( CAT , LEVEL )		\
												namespace { \
													xeres::TraceCategory __traceCategory( CAT , xeres::LEVEL ); \
													xeres::ValueInitor< xeres::TraceCategory * >\
													__initor( xeres::__pTraceCategory , &__traceCategory ); \
												} \

#	define	TRACE_WITH_LEVEL(LEVEL)				xeres::TraceOutput( xeres::LEVEL , xeres::__pTraceCategory ).Trace

#else

#	define	TRACE_CATEGORY( CAT , LEVEL )

#	define	TRACE_WITH_LEVEL(LEVEL)				__noop

#endif /*_DEBUG*/

	/// \brief	Output trace at TRACE_LV_TRACE level. \sa TraceLevel
#define	TRACE_TRACE					TRACE_WITH_LEVEL( TRACE_LV_TRACE )
	/// \brief	Output trace at TRACE_LV_DEBUG level. \sa TraceLevel
#define	TRACE_DEBUG					TRACE_WITH_LEVEL( TRACE_LV_DEBUG )
	/// \brief	Output trace at TRACE_LV_INFO level. \sa TraceLevel
#define	TRACE_INFO					TRACE_WITH_LEVEL( TRACE_LV_INFO )
	/// \brief	Output trace at TRACE_LV_WARNING level. \sa TraceLevel
#define	TRACE_WARNING				TRACE_WITH_LEVEL( TRACE_LV_WARNING )
	/// \brief	Output trace at TRACE_LV_ERROR level. \sa TraceLevel
#define	TRACE_ERROR					TRACE_WITH_LEVEL( TRACE_LV_ERROR )

	/// \brief	Output trace at TRACE_LV_FATAL level. \sa TraceLevel
#define	TRACE_FATAL					TRACE_WITH_LEVEL( TRACE_LV_FATAL )

	/// \brief	Output trace at TRACE_LV_CONSOLE level. \sa TraceLevel
#define	TRACE_CONSOLE				TRACE_WITH_LEVEL( TRACE_LV_CONSOLE )

	/// \brief	Output trace at TRACE_LV_ASSERT level. \sa TraceLevel
#define	TRACE_ASSERT				TRACE_WITH_LEVEL( TRACE_LV_ASSERT )

	/// \brief	Raise a fatal exception and abort program.
#define	FATAL_ABORT( MSG )			fatal_abort( MSG , __FILEW__ , __FUNCTIONW__ )

	// assert functions

	/*!
		\brief		Raise an exception to make program terminated by system.
	*/
	void raise_exception( void );

	/*!
		\def	XS_ASSERT( _Expression )
		\brief	Assertion, like assert in CRT, but still executes in Release version.
	*/

	/*!
		\def	XS_VERIFY( _Expression )
		\brief	Cause assertion at Debug version, and executes expression without assertion in Release version.
	*/

#ifndef _RELEASE

#define	XS_ASSERT( _Expression )			\
		if( !(_Expression) )				\
		{									\
			TRACE_ASSERT( _S("ASSERTION FAILED: ") _WIDE_STR(#_Expression) _S("\n") \
				_S("<%s>(#%d) %s") , __FILEW__ , (int)__LINE__ , __FUNCTIONW__ ); \
			assert( _Expression );				\
		} \

#define	XS_VERIFY( _Expression )		XS_ASSERT( _Expression )

#else
	// FIXME! Should we need to raise exception to send error report?
#define	XS_ASSERT( _Expression )		\
		if( !(_Expression) )			\
		{								\
			raise_exception();			\
		} \

#define	XS_VERIFY( _Expression )		(_Expression)

#endif /*_RELEASE*/

#if XERES_DEBUG_ENABLE_TRACKING

	// Tracking function scopes
	// Insert this macro at the significant entry of module to trace call graph.

	/*!
		\def	TRACK_FUNCTION_SCOPE()
		\brief	Starts a tracking record of function, used to track call stack. 

		\def	TRACK_FUNCTION_SCOPE_MSG
		\brief	Starts a tracking record of function, used to track call stack.
				This version can follow up with an argument list, which can be
				serialized in tracking information as detail message.
		\eg How to track script functions.
		\code
			// Calling in a script call wrapper.
			TRACEK_FUNCTION_SCEOP_MSG( _S("In script call '%s'.") , scriptFunctionName.c_str() );
			// And so on ...
		\endcode
	*/

	class TrackFunctionScope
	{
	public:

		/// \ctor
		TrackFunctionScope( const wchar_t * function , const wchar_t * file , const int line );

		/// \dtor
		~TrackFunctionScope( void );

		//Make tracker of function scope insert specific message.
		void WithMessage( const wchar_t * fmt , ... );
	};

#define	TRACK_FUNCTION_SCOPE()			xeres::TrackFunctionScope __trackFunctionScope( \
										__FUNCTIONW__ , __FILEW__ , (int)__LINE__ )

	// usually for scripting debug
#define	TRACK_FUNCTION_SCOPE_MSG		xeres::TrackFunctionScope __trackFunctionScope( \
										__FUNCTIONW__ , __FILEW__ , (int)__LINE__ ); \
										__trackFunctionScope.WithMessage

	// for scope track entrance
	void track_scope_begin( const wchar_t * function , const wchar_t * file , const int line );
	
	// for scope track exit
	void track_scope_end( void );

	// attach msg to current function tracker
	void track_scope_msgv( const wchar_t * fmt , va_list ap );
	void track_scope_msg( const wchar_t * fmt , ... );

	// get current track depth
	int track_scope_depth( void );

	// dump call stack
	void track_scope_dump( FILE * file );

	// clear all call track.
	void track_scope_clear_all( void );

#else
#define	TRACK_FUNCTION_SCOPE()
#define	TRACK_FUNCTION_SCOPE_MSG			__noop
#endif /*XERES_DEBUG_ENABLE_TRACKING*/

} // namespace xeres
